use actix_web::{error, get, post, put, web, Error, HttpRequest, HttpResponse, HttpServer, Result};
use futures_util::StreamExt as _;
use json::JsonValue;
use redis::AsyncCommands;
use serde::{Deserialize, Serialize};

use crate::models::{
    response::ResponseBody,
    score::{ScoreBody, ScoreObj, ScoreParam},
};

#[get("")]
pub async fn get_score(
    query: web::Query<ScoreParam>,
    redis: web::Data<redis::Client>,
    req: HttpRequest,
) -> Result<HttpResponse> {
    let param: ScoreParam = query.0;

    let key = param.key;
    let start = param.start;
    let end = param.end;

    let mut conn = redis
        .get_tokio_connection_manager()
        .await
        .map_err(error::ErrorInternalServerError)?;

    let res = redis::Cmd::zrevrange_withscores(&key, start, end)
        .query_async::<_, Vec<String>>(&mut conn)
        .await
        .unwrap();
    // .map_err(error::ErrorInternalServerError)?;

    let mut score_res = vec![];
    let mut res_id = 1;

    if res.len() > 0 {
        res.chunks(2).for_each(|v| {
            let vs = v.iter().map(|x| x.to_string()).collect::<Vec<_>>();
            let score_obj = ScoreObj {
                id: res_id,
                name: vs[0].to_owned(),
                score: vs[1].to_owned(),
            };
            score_res.push(score_obj);
            res_id = res_id + 1;
        });
    }
    Ok(HttpResponse::Ok().json(ResponseBody::new("ok", score_res)))

    // if res.len() > 0 {
    //     Ok(HttpResponse::Ok().json(ResponseBody::new("ok", score_res)))
    // } else {
    //     Ok(HttpResponse::InternalServerError().finish())
    // }
}

#[post("")]
pub async fn post_score(
    item: web::Json<ScoreBody>,
    redis: web::Data<redis::Client>,
    req: HttpRequest,
) -> Result<HttpResponse> {
    // println!("request: {req:?}");
    // println!("model: {item:?}");

    let body: ScoreBody = item.0;

    let key = body.key;
    let name = body.name;
    let score = body.score;

    let mut conn = redis
        .get_tokio_connection_manager()
        .await
        .map_err(error::ErrorInternalServerError)?;

    let res = redis::Cmd::zadd(&key, name, score)
        .query_async::<_, u8>(&mut conn)
        .await
        .unwrap();
    // .map_err(error::ErrorInternalServerError)?;

    if res == 1 || res == 0 {
        Ok(HttpResponse::Ok().json(ResponseBody::new("ok", res)))
    } else {
        Ok(HttpResponse::InternalServerError().finish())
    }
}

#[put("")]
pub async fn put_score(
    item: web::Json<ScoreBody>,
    redis: web::Data<redis::Client>,
) -> Result<HttpResponse> {
    let body: ScoreBody = item.0;
    let key = body.key;
    let name = body.name;
    let score = body.score;

    let mut conn = redis
        .get_tokio_connection_manager()
        .await
        .map_err(error::ErrorInternalServerError)?;

    let res = redis::Cmd::zincr(&key, name, score)
        .query_async::<_, String>(&mut conn)
        .await
        .unwrap();

    if res != "" {
        Ok(HttpResponse::Ok().json(ResponseBody::new("ok", res)))
    } else {
        Ok(HttpResponse::InternalServerError().finish())
    }
}

// routes
pub fn router(config: &mut web::ServiceConfig) {
    config.service(
        web::scope("/api/score")
            .service(get_score)
            .service(post_score)
            .service(put_score),
    );
}
